package com.ljph.seckill.service;

import com.jfinal.aop.Before;
import com.jfinal.plugin.activerecord.ActiveRecordException;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.activerecord.tx.Tx;
import com.ljph.seckill.dto.Exposer;
import com.ljph.seckill.dto.SeckillExecution;
import com.ljph.seckill.enums.SeckillState;
import com.ljph.seckill.exception.RepeatKillException;
import com.ljph.seckill.exception.SeckillCloseException;
import com.ljph.seckill.exception.SeckillException;
import com.ljph.seckill.model.Seckill;
import com.ljph.seckill.model.SuccessKilled;
import com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.List;

/**
 * Created by yuzhou on 16/8/27.
 */
public class SeckillService {

    private static final Logger _log = LoggerFactory.getLogger(SeckillService.class);
    // 用于生成md5时混淆
    private static final String salt = "dfsafjkjoijqerfdfkdj980{P{POpdfdf";

    private String getMD5(Long seckillId) {
        String base = seckillId + "/" + salt;
        String md5 = DigestUtils.md5Hex(base);
        return md5;
    }

    /**
     * 查询所有秒杀记录
     * @return
     */
    public List<Seckill> getSeckillList() {

        return Seckill.dao.findAll(0, 4);
    }

    /**
     * 查询单个秒杀记录
     * @param seckillId
     * @return
     */
    public Seckill getById(Long seckillId) {
        return Seckill.dao.findById(seckillId);
    }

    /**
     * 秒杀开启时输出秒杀接口地址，否则输出系统时间和秒杀时间
     * 秒杀开启之前应该谁也猜不出我们的秒杀地址
     * @param seckillId
     * @return
     */
    public Exposer exportSeckillUrl(Long seckillId) {

        Seckill seckill = Seckill.dao.findById(seckillId);

        if(seckill == null) {
            return new Exposer(false, seckillId);
        }

        Date startTime = seckill.getStartTime();
        Date endTime = seckill.getEndTime();

        Date now = new Date();

        if(now.getTime() < startTime.getTime() || now.getTime() > endTime.getTime()) {

            return new Exposer(false, seckillId, now, startTime, endTime);
        }

        String md5 = getMD5(seckillId);
        return new Exposer(true, md5, seckillId, now);
    }


    /**
     * 执行秒杀操作
     * @param seckillId
     * @param userPhone
     * @param md5
     */
    @Before({Tx.class})
    public SeckillExecution executeSeckill(Long seckillId, Long userPhone, String md5) throws SeckillException, RepeatKillException, SeckillCloseException {

        if(md5 == null || !md5.equals(getMD5(seckillId))) {
            throw new SeckillException("seckill data rewrite");
        }

        //执行秒杀逻辑：减库存 + 记录购买行为
        Date now = new Date();
        int updated = Seckill.dao.reduceNumber(seckillId, now);

        if(updated <= 0) { //没有更新到记录，秒杀结束
            throw new SeckillCloseException("seckill is closed");
        } else { //记录购买行为
            SuccessKilled successKilled = new SuccessKilled();
            successKilled.setSeckillId(seckillId);
            successKilled.setUserPhone(userPhone);
            successKilled.setState(0);
            successKilled.setCreateTime(now);

            try{
                if(successKilled.save()) {
                    return new SeckillExecution(seckillId, SeckillState.SUCCESS, successKilled);
                } else {
                    throw new RuntimeException("inner error");
                }
            } catch (ActiveRecordException are) {
                Throwable cause = are.getCause();
                if(cause instanceof MySQLIntegrityConstraintViolationException) {
                    // cause.getMessage() TODO: use pattern validate if message match "Duplicate entry '1003-13776573631' for key 'PRIMARY'"
                    throw new RepeatKillException("repeat seckill");
                } else {
                    throw are;
                }
            }
        }
    }
}
